#include <stdio.h>
#include "xdisk.h"
#include "xfat.h"
#include <time.h>

xfat_err_t xdisk_open(xdisk_t* disk, const char* name, xdisk_driver_t* driver, void* init_data, u8_t* disk_buf, u32_t buf_size) {
	xfat_err_t err;
	
	xfat_obj_init(&(disk->obj), XFAT_OBJ_DISK);
	disk->driver = driver;
	err = disk->driver->open(disk, init_data);
	if (err < 0) {
		return err;
	}
	err = xfat_bpool_init(&(disk->obj), disk->sector_size, disk_buf, buf_size);
	if (err < 0) return err;

	disk->name = name;
	return 0;
}


xfat_err_t xdisk_close(xdisk_t* disk) {
	xfat_err_t err;
	err = xfat_bpool_flush(to_base_type(disk));
	if (err < 0) return err;
	err = disk->driver->close(disk);
	if (err < 0) {
		return err;
	}
	return 0;
}

xfat_err_t xdisk_read_sector(xdisk_t* disk, u8_t* buffer, u32_t start_sector, u32_t count) {
	xfat_err_t err;
	if (start_sector + count >= disk->total_sector) {
		return FS_ERR_PARAM;
	}
	err = disk->driver->read_sector(disk, buffer, start_sector, count);
	return err;
}

xfat_err_t xdisk_write_sector(xdisk_t* disk, u8_t* buffer, u32_t start_sector, u32_t count) {
	xfat_err_t err;
	if (start_sector + count >= disk->total_sector) {
		return FS_ERR_PARAM;
	}

	err = disk->driver->write_sector(disk, buffer, start_sector, count);
	return err;
}


static xfat_err_t xdisk_hw_open(xdisk_t* disk, void* init_data) {
	const char* path = (const char*)init_data;
	FILE* file = fopen(path, "rb+");
	if (file == NULL) {
		printf("open disk failed:%s\n", path);
		return FS_ERR_IO;
	}
	
	disk->data = file;
	disk->sector_size = 512;
	
	fseek(file, 0, SEEK_END);
	disk->total_sector = ftell(file) / disk->sector_size;
	return FS_ERR_OK;
}

static xfat_err_t xdisk_hw_close(xdisk_t* disk) {
	FILE* file = (FILE*)disk->data;
	int err = (int)fclose(file);
	if (err == -1) {
		return FS_ERR_IO;
	}
	return FS_ERR_OK;
}

static xfat_err_t xdisk_hw_read_sector(xdisk_t* disk, u8_t* buffer, u32_t start_sector, u32_t count) {
	u32_t offset = start_sector * disk->sector_size;
	FILE* file = (FILE*)disk->data;
	
	int err = fseek(file, offset, SEEK_SET);
	if (err == -1) {
		printf("seek disk failed: 0x%x\n", (int)offset);
		return FS_ERR_IO;
	}

	err = (int)fread(buffer, disk->sector_size, count, file);
	if (err == -1) {
		printf("read disk failed: sector %d, count %d\n", (int)start_sector, (int)count);
		return FS_ERR_IO;
	}
	return FS_ERR_OK;
}

static xfat_err_t xdisk_hw_write_sector(xdisk_t* disk, u8_t* buffer, u32_t start_sector, u32_t count) {
	u32_t offset = start_sector * disk->sector_size;
	FILE* file = (FILE*)disk->data;
	
	int err = fseek(file, offset, SEEK_SET);
	if (err == -1) {
		printf("seek disk failed: 0x%x\n", (int)offset);
		return FS_ERR_IO;
	}

	err = (int)fwrite(buffer, disk->sector_size, count, file);
	if (err == -1) {
		printf("write disk failed: sector %d, count %d\n", (int)start_sector, (int)count);
		return FS_ERR_IO;
	}
	fflush(file);
	return FS_ERR_OK;
}

static xfat_err_t xdisk_hw_curr_time(xdisk_t* disk, xfile_time_t* timeinfo) {
	time_t raw_time;
	time(&raw_time);
	
	struct tm* _localtime = localtime(&raw_time);
	timeinfo->year = _localtime->tm_year + 1900;
	timeinfo->month = _localtime->tm_mon + 1;
	timeinfo->day = _localtime->tm_mday;
	timeinfo->hour = _localtime->tm_hour;
	timeinfo->minute = _localtime->tm_min;
	timeinfo->second = _localtime->tm_sec;

	return FS_ERR_OK;
	
}

xdisk_driver_t vdisk_driver = {
	.open = xdisk_hw_open,
	.close = xdisk_hw_close,
	.curr_time = xdisk_hw_curr_time,
	.read_sector = xdisk_hw_read_sector,
	.write_sector = xdisk_hw_write_sector,
};